/*
 * Copyright 2009 BioTeam Inc
 * 
 * Licensed under License GNU LESSER GENERAL PUBLIC LICENSE version 3.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.gnu.org/copyleft/lesser.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.bioteam.appweb.gwt.apps.server.program;

import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.bioteam.appweb.Config;
import net.bioteam.appweb.gwt.apps.clientandserver.Constant;
import net.bioteam.appweb.gwt.apps.server.UnsupportedUserFileTypeException;
import net.bioteam.appweb.gwt.apps.server.UserFileConverter;
import net.bioteam.appweb.model.Parameter;
import net.bioteam.appweb.model.ParameterType;
import net.bioteam.appweb.model.Program;
import net.bioteam.appweb.model.ProgramBuilder;
import net.bioteam.appweb.model.ProgramTemplate;
import net.bioteam.appweb.submit.ProgramValueBean;
import net.bioteam.appweb.submit.SubmitData;
import net.bioteam.appweb.submit.SubmitterUtil;
import net.bioteam.appweb.submit.UnfinishedJob;
import net.bioteam.appweb.submit.SubmitData.SubmitSource;
import net.bioteam.common.user.User;
import net.bioteam.common.utils.MyEntityManager;
import net.bioteam.common.utils.Utils;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Servlet implementation class ProgramHandler
 */
public class ProgramHandler extends HttpServlet
{
	private static final long serialVersionUID = 1L;
	public static final Log logger = LogFactory.getLog(ProgramHandler.class);
	public static long MAX_FILE_SIZE_TO_SAVE = 1024 * 1024;

	public ProgramHandler()
	{
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException
	{

		User user = (User) request.getSession().getAttribute("user");
		// Parse the request
		File dir = new File(Config.USER_TMP + File.separator + user.getName());
		if (!dir.exists())
		{
			dir.mkdirs();
		}
		List<File> tempFiles = new LinkedList<File>();
		try
		{
			Map<String, String> values = retrieveValue(request, dir, user, tempFiles);
			String msg = null;
			Program program=ProgramBuilder.getProgram(values.get("_program"));
			postProcessValue(values, program);

			if (values.get(Constant.program_form_action_name).equals(Constant.run_action))
			{
				UnfinishedJob runStatus = processRun(values, user, null);
				msg = getUploadRetMessage(true, null, "user_jobs_"+runStatus.getId());
			} else
			{
				ProgramTemplate run = processSave(values, user);
				msg = getUploadRetMessage(true, null, "user_templates");
			}
			
			resp.setContentType("text/html");
			resp.setContentLength(msg.length());
			resp.getWriter().write(msg);
		} catch (Exception e)
		{

			String msg = getUploadRetMessage(false, e.getMessage(), null);
			resp.setContentType("text/html");
			resp.setContentLength(msg.length());
			resp.getWriter().write(msg);
		} finally
		{
			for (File file : tempFiles)
			{
				if (file.exists())
					file.delete();
			}
		}

	}
	
	public void postProcessValue(Map<String, String> values, Program program)
	{
		for(Parameter param: program.getAllParameters())
		{
			if (param.getType()==ParameterType.BooleanType)
			{
				if (values.containsKey(param.getName()))
				{
					if (values.get(param.getName()).equals("true"))
					{
						values.put(param.getName(), "1");
					} else
					{
						values.put(param.getName(), "0");
					}
				}
			} else if (param.getType()==ParameterType.SingleSelectionType)
			{
				if (values.containsKey(param.getName()))
				{
					if (values.get(param.getName()).equals("undefined"))
					{
						values.remove(param.getName());
					}
				}
			} else if (param.getType()==ParameterType.MultipleSelectionType)
			{
				if (values.containsKey(param.getName()) && !param.getSeparator().equals(","))
				{
					values.put(param.getName(), values.get(param.getName()).replaceAll(",", param.getSeparator()));
				}
			}
		}
	}

	public Map<String, String> retrieveValue(HttpServletRequest request, File dir, User user, List<File> tempfiles) throws Exception
	{
		ServletFileUpload upload = new ServletFileUpload();
		Map<String, String> values = new HashMap<String, String>();

		FileItemIterator iter = null;
		iter = upload.getItemIterator(request);
		while (iter.hasNext())
		{
			FileItemStream item = iter.next();
			String name = item.getFieldName();
			if (name.startsWith("_temp_"))
			{
				continue;
			}
			InputStream stream = item.openStream();

			String filename = item.getName();
			if (filename != null)
			{
				filename = FilenameUtils.getName(filename.trim());
			}

			if (item.isFormField())
			{
				if (name.endsWith(Constant.file_data_suffix))
				{
					String paramName = name.substring(0, name.length() - Constant.file_data_suffix.length());
					File file = new File(dir.getAbsolutePath() + File.separator + paramName);
					tempfiles.add(file);
					BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
					Streams.copy(stream, out, true);
					if (file.length() == 0)
					{
						// empty element
						file.delete();
					} else
					{
						values.put(paramName, file.getAbsolutePath());
					}
				} else if (name.endsWith(Constant.file_server_suffix))
				{
					String value = Streams.asString(stream).trim();
					if (value.length() > 0)
					{
						String paramName = name.substring(0, name.length() - Constant.file_server_suffix.length());
						value = webStringToRealFile(value, user.getName());
						values.put(paramName, value);
					}
				} else if (name.endsWith(Constant.file_usa_suffix))
				{
					String value = Streams.asString(stream).trim();
					if (value.length() > 0)
					{
						String paramName = name.substring(0, name.length() - Constant.file_usa_suffix.length());
						//value = webStringToRealFile(value, user.getName());
						values.put(paramName, value);
					}
				} 
				
				else
				{
					String value = Streams.asString(stream).trim();
					if (value.length() > 0)
					{
						if (values.containsKey(name))
						{
							values.put(name, values.get(name)+","+value);
						} else
						{
							values.put(name, value);
						}
					}
				}
			} else
			{
				if (filename == null || filename.length() == 0)
					continue;
				File file = new File(dir.getAbsolutePath() + File.separator + filename);
				tempfiles.add(file);
				BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
				Streams.copy(stream, out, true);
				if (file.length() == 0)
				{
					// empty element
					file.delete();
				} else
				{
					values.put(name, file.getAbsolutePath());
				}

			}
		}
		return values;
	}

	public static UnfinishedJob processRun(Map<String, String> values, User user, String serviceID) throws Exception
	{
		String programName = values.get(Constant.progranm_form_program_name);
		Program program = ProgramBuilder.getProgram(programName);
		//perform by postprocess
//		for (Parameter param : program.getAllParameters())
//		{
//			if (param.getType() == ParameterType.MultipleSelectionType)
//			{
//				String separator = param.getSeparator();
//				String value = values.get(param.getName());
//				if (value == null)
//				{
//					continue;
//				}
//				if (separator.equals(" "))
//				{
//					value = value.replaceAll("\" \"", " ");
//					values.put(param.getName(), value);
//				}
//			} else if (param.getType() == ParameterType.BooleanType)
//			{
//				if (values.get(param.getName()) == null)
//				{
//					values.put(param.getName(), "0");
//				} else
//				{
//					values.put(param.getName(), "1");
//				}
//			}
//		}
		for (Map.Entry<String, String> entry : values.entrySet())
		{
			logger.info(entry.getKey() + "=" + entry.getValue());
		}
		ProgramValueBean bean = new ProgramValueBean(program);
		bean.setValueMap(values);

		SubmitData data = new SubmitData();
		data.setBean(bean);
		data.setProgram(bean.getProgram());
		data.setUser(user);
		if (serviceID == null)
		{
			data.setSubmitSource(SubmitSource.WEB);
		} else
		{
			data.setSubmitSource(SubmitSource.WEBDATA);
			data.setServiceID(serviceID);
		}
		return SubmitterUtil.submitJob(data);
	}

	
	public static ProgramTemplate processSave(Map<String, String> values, User user) throws Exception
	{
		String programName = values.get(Constant.progranm_form_program_name);
		Program program = ProgramBuilder.getProgram(programName);
		for (Parameter param : program.getAllParameters())
		{
			String value = values.get(param.getName());
			if (value == null)
			{
				continue;
			}
//			if (param.getType() == ParameterType.MultipleSelectionType)
//			{
//				String separator = param.getSeparator();
//				if (separator.equals(" "))
//				{
//					value = value.replaceAll("\" \"", " ");
//					values.put(param.getName(), value);
//				}
//			} else if (param.getType() == ParameterType.BooleanType)
//			{
//				if (values.get(param.getName()) == null)
//				{
//					values.put(param.getName(), "0");
//				} else
//				{
//					values.put(param.getName(), "1");
//				}
//			} else 
				if (param.getType() == ParameterType.InputFileType || param.getType() == ParameterType.SequenceType)
			{
				// temporary file, ethier file from client or text pasted from
				// client.
				if (value.startsWith(Config.USER_TMP))
				{
					File file = new File(value);
					// ignore big data.
					if (file.length() <= MAX_FILE_SIZE_TO_SAVE)
					{
						String content = FileUtils.readFileToString(new File(value));
						values.put(param.getName() + Constant.file_data_suffix, content);
						values.remove(param.getName());
					}
				} else
				{
					value = realFileToWebString(value, user.getName());
					// this is a server file.
					values.put(param.getName() + Constant.file_server_suffix, value);
					values.remove(param.getName());

				}

			}
		}
		for (Map.Entry<String, String> entry : values.entrySet())
		{
			logger.info(entry.getKey() + "=" + entry.getValue());
		}
		File savedDir = new File(Config.topdir + File.separator + Config.TEMPLATE_DIR + File.separator + user.getName());
		if (!savedDir.exists())
		{
			savedDir.mkdirs();
		}

		StringBuffer sb = new StringBuffer();
		Formatter formatter = new Formatter(sb);
		Date date = new Date();
		formatter.format("_%tY%tm%td_%tH%tM%tS", date, date, date, date, date, date);

		String fileName = savedDir.getAbsolutePath() + File.separator + program.getName() + sb.toString();

		XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(fileName)));
		e.writeObject(values);
		e.close();

		ProgramTemplate run = new ProgramTemplate();
		run.setName(program.getName());
		run.setProgramName(program.getName());
		run.setSavedTime(date);
		run.setUsername(user.getIdAsString());
		EntityManager em = MyEntityManager.getEntityManager();
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		em.persist(run);
		tx.commit();
		return run;
	}

	public static String webStringToRealFile(String value, String username) throws UnsupportedUserFileTypeException
	{
//		if (value.startsWith("/"+Constant.server_file_public_name))
//		{
//			value = Config.USER_FILE + File.separator + Config.PUBLIC_FILE_DIR + value.substring(Constant.server_file_public_name.length()+1);
//		} else
//		{
//			// start with user.
//			String path = value.substring(Constant.server_file_user_name.length()+1);
//			value = UserFileConverter.clientPathToRealPath(path, username);
//		}
//		return value;
//		
		return UserFileConverter.clientPathToRealPath(value, username);
	}

	public static String realFileToWebString(String filename, String username) throws UnsupportedUserFileTypeException
	{
//		if (filename.startsWith(Config.USER_FILE + File.separator + Config.PUBLIC_FILE_DIR))
//		{
//			filename = Constant.server_file_public_name + filename.substring(Config.USER_FILE.length() + Config.PUBLIC_FILE_DIR.length() + 1);
//		} else
//		{
//			filename = Constant.server_file_user_name + UserFileConverter.realPathToClientPath(filename, username);
//		}
//		return filename;
		
		return UserFileConverter.realPathToClientPath(filename, username);
	}

	public static String getUploadRetMessage(boolean success, String msg, String token)
	{
		JSONObject obj = new JSONObject();
		try
		{
			obj.put("status", success);
			if (msg != null)
				obj.put("msg", msg);
			if (token != null)
				obj.put("token", token);
		} catch (JSONException e)
		{
			// impossible to occur.
		}

		return "<html><head></head><body><script type='text/javascript'>window.top._jasonUploadCallback('" + StringEscapeUtils.escapeJavaScript(obj.toString()) + "');</script></body>";
	}

}
